package cn.geminis.crypto.core.util;

import cn.geminis.core.exception.CommonException;
import lombok.NonNull;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Objects;

/**
 * @author Allen
 */
public class ObjectUtils {

    private ObjectUtils() {
    }

    public static Object getValue(@NonNull Object object, @NonNull String fieldPath) {
        var fieldNames = fieldPath.split("\\.");
        var clazz = object.getClass();
        return getValue(clazz, object, fieldNames, 0);
    }

    public static Object getValue(@NonNull Class<?> clazz, @NonNull String fieldPath) {
        var fieldNames = fieldPath.split("\\.");
        return getValue(clazz, null, fieldNames, 0);
    }

    private static Object getValue(Class<?> clazz, Object object, String[] fieldNames, int nameIndex) {
        var fieldName = fieldNames[nameIndex];
        var field = getField(clazz, fieldName);
        if (!field.canAccess(object)) {
            field.setAccessible(true);
        }

        Object fieldValue;
        try {
            fieldValue = field.get(object);
        } catch (IllegalAccessException e) {
            throw new InternalError("获取对象属性错误，属性：" + fieldName, e);
        }

        nameIndex++;
        if (Objects.isNull(fieldValue)) {
            return null;
        } else if (nameIndex < fieldNames.length) {
            return getValue(clazz, fieldValue, fieldNames, nameIndex);
        } else {
            return fieldValue;
        }
    }

    public static Field getField(@NonNull Class<?> clazz, @NonNull String fieldName) {
        var originalClass = clazz;
        while (null != clazz) {
            var fields = clazz.getDeclaredFields();
            var found = Arrays.stream(fields).filter(f -> f.getName().equals(fieldName)).findAny();
            if (found.isPresent()) {
                return found.get();
            } else {
                clazz = clazz.getSuperclass();
            }
        }
        throw new CommonException("类型不包含字段，Class：" + originalClass.getName() + "，Field：" + fieldName);
    }

    private static void setValue(Class<?> clazz, Object object, String fieldPath, Object value) {
        var fieldNames = fieldPath.split("\\.");
        var getFieldNames = Arrays.copyOfRange(fieldNames, 0, fieldNames.length - 1);
        var fieldParent = object;
        var classParent = clazz;
        if (getFieldNames.length > 0) {
            fieldParent = getValue(clazz, object, getFieldNames, 0);
            if (Objects.isNull(fieldParent)) {
                throw new IllegalArgumentException("属性指向的对象为空");
            }
            classParent = fieldParent.getClass();
        }
        var fieldName = fieldNames[fieldNames.length - 1];
        var field = getField(classParent, fieldName);
        if (!field.canAccess(object)) {
            field.setAccessible(true);
        }
        try {
            field.set(fieldParent, value);
        } catch (IllegalAccessException e) {
            throw new InternalError("设置对象属性错误，属性：" + fieldName, e);
        }
    }

    public static void setValue(Object object, String fieldPath, Object value) {
        setValue(object.getClass(), object, fieldPath, value);
    }

}
